# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
import time
from pydantic import TypeAdapter
from tortoise.queryset import Q
from tortoise.transactions import in_transaction
from hypertext import PagingResult
from exception import AppException
from common.utils.urls import UrlUtil
from common.enums.pay import PayEnum
from common.enums.wallet import WalletEnum
from common.models.users import UserModel
from common.models.users import UserWalletModel
from common.models.market import RechargeOrderModel
from common.service.refund_logic import RefundLogic
from apps.admin.schemas.finance import recharge_schema as schema


class RechargeService:

    @classmethod
    async def lists(cls, params: schema.RechargeSearchIn) -> PagingResult[schema.RechargeListVo]:
        """
        充值记录列表。

        Args:
            params (schema.RechargeSearchIn): 充值记录查询参数。

        Returns:
            PagingResult[schema.RechargeListVo]: 充值记录分页列表Vo。

        Author:
            zero
        """
        where = RechargeOrderModel.build_search({
            "=": ["pay_way"],
            "%like%": ["order_sn"],
            "datetime": ["start_time|end_time@create_time"]
        }, params.__dict__)
        where.append(Q(pay_status=PayEnum.PAID_OK))

        if params.user:
            user = await (UserModel
                          .filter(is_delete=0)
                          .filter(Q(sn=params.user) | Q(mobile=params.user) | Q(nickname=params.user))
                          .limit(300)
                          .values("id"))

            user_ids = [item["id"] for item in user if item["id"]] or [0]
            where.append(Q(user_id__in=list(set(user_ids))))

        _model = RechargeOrderModel.filter(*where).order_by("-id")
        _pager = await RechargeOrderModel.paginate(
            model=_model,
            page_no=params.page_no,
            page_size=params.page_size,
            datetime_field=["create_time", "pay_time"]
        )

        user_ids = [item["user_id"] for item in _pager.lists if item["user_id"]]
        users: dict = await UserModel.queryUserDictsById(user_ids)

        list_vo = []
        for item in _pager.lists:
            user = users.get(item["user_id"]) or {}
            pack = json.loads(item["snapshot"] or "{}")
            item["pack_name"] = pack.get("name") or "-"
            item["pay_time"] = item["pay_time"] or "-"
            item["create_time"] = item["create_time"] or "-"
            item["pay_way"] = PayEnum.get_pay_way_msg(item["pay_way"])
            item["user"] = {
                "sn": user.get("sn", ""),
                "avatar": await UrlUtil.to_absolute_url(user.get("avatar", "")),
                "nickname": user.get("nickname", ""),
                "mobile": user.get("mobile", "")
            }

            vo = TypeAdapter(schema.RechargeListVo).validate_python(item)
            list_vo.append(vo)

        _pager.lists = list_vo
        _pager.extend = {
            "pay_way": PayEnum.get_pay_way_msg(True),
            "pay_status": PayEnum.get_pay_status_msg(True)
        }
        return _pager

    @classmethod
    async def refund(cls, order_id: int, admin_id: int):
        order = await RechargeOrderModel.filter(id=order_id, is_delete=0).get()
        user = await UserModel.filter(id=order.user_id, is_delete=0).first()

        if not user:
            raise AppException("用户已不存在")

        if order.pay_status == PayEnum.PAID_NO:
            raise AppException("订单尚未支付")

        if order.refund_status == PayEnum.REFUND_OK:
            raise AppException("订单已退款了")

        if order.refund_status == PayEnum.REFUND_ING:
            raise AppException("订单退款中,请勿重复操作")

        try:
            async with in_transaction("mysql"):
                # 倒扣充值
                points_num = max(user.points - order.points_num, 0)
                train_num = max(user.train_num - order.train_num, 0)
                robot_num = max(user.robot_num - order.robot_num, 0)
                know_num = max(user.know_num - order.know_num, 0)
                mcp_num = max(user.mcp_num - order.mcp_num, 0)
                await UserModel.filter(id=user.id).update(
                    points=points_num,
                    train_num=train_num,
                    robot_num=robot_num,
                    know_num=know_num,
                    mcp_num=mcp_num
                )

                # 记录流水
                enum = WalletEnum
                flow = [
                    {"v": points_num, "s": enum.POINTS, "t": enum.POINTS_DEC_RECHARGE},
                    {"v": train_num, "s": enum.TRAIN, "t": enum.TRAIN_DEC_RECHARGE},
                    {"v": robot_num, "s": enum.ROBOT, "t": enum.ROBOT_DEC_RECHARGE},
                    {"v": know_num, "s": enum.KNOW, "t": enum.KNOW_DEC_RECHARGE},
                    {"v": mcp_num, "s": enum.MCP, "t": enum.MCP_DEC_RECHARGE},
                ]
                for item in flow:
                    if item["v"] > 0:
                        await UserWalletModel.inc(
                            scene=item["s"],
                            user_id=user.id,
                            change_type=item["t"],
                            change_amount=item["v"]
                        )

                # 更新订单
                await RechargeOrderModel.filter(id=order.id).update(
                    refund_status=PayEnum.REFUND_ING,
                    update_time=int(time.time())
                )

                # 发起退款
                await RefundLogic.refund(
                    scene="recharge",
                    order_id=order.id,
                    refund_amount=order.paid_amount,
                    admin_id=admin_id
                )
        except Exception as e:
            raise AppException(str(e))
